﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Zhaoxi.ThreadShow
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        #region 多线程：Thread:

        /// <summary>
        /// 多线程：Thread:
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ThreadBtn_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"****************ThreadBtn_Click Start  ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            //多线程就是从这里开始的  
            //Thread thread = new Thread(() => Console.WriteLine("欢迎大家来到高级本的体验课！"));


            //Thread thread = new Thread(() =>
            //{
            //    this.DoCostTimeMethod("fresh");
            //});
            //thread.Start();//开启了一个线程

            {
                //Thread thread = new Thread(o => Console.WriteLine($"这里是开启了一个线程 {Thread.CurrentThread.ManagedThreadId.ToString("00")} 结果为：{o}"));
                //thread.Start("Joer2鹏");
            }
            {
                //thread.Suspend();//暂停
                //thread.Resume();//让暂停的线程继续执行。。。
                //thread.Abort();//线程要终止
                //Thread.ResetAbort();//终止的线程可以继续运行
                //Thread.Sleep(2000); //主线程等待2秒钟；
                //thread.Join();//thread等待等待 代码执行到这里会卡住
                //thread.Join(2000); //限时等待  过时不候
                //while (thread.ThreadState!=ThreadState.Stopped)
                //{
                //    Thread.Sleep(2000);
                //} 
                //thread.Priority = ThreadPriority.Highest;//最好不通过他来控制线程 只能从概念上来增加他的优先级
                //thread.IsBackground = true;//进程关闭  线程也就随之取消
                //thread.IsBackground = false;
            }
            {
                //While  await 他会卡顿界面；
                //Thread 没有线程的Api来控制线程顺序 
                //使用多线程：不会卡顿界面；
                //关于委托的详解：大家添加一下课堂的助教微信 有专门的委托课程分享；

                //Action threadStart = new Action(() =>
                //{
                //    this.DoCostTimeMethod("fresh");
                //});
                //Action action = () => Console.WriteLine("这是上一个线程执行完毕以后执行的动作");
                //this.ThreadWithCallBack(threadStart, action);
                ////如果我需要返回值呢？ 
                Func<int> func = () =>
                {
                    return DateTime.Now.Year;
                };
                //int iResult = ThreadWithReturn(func); 
                Func<int> fResult = ThreadWithReturn(func);
                Console.WriteLine("this is ....");
                Console.WriteLine("this is ....");
                Console.WriteLine("this is ....");
                int iResult = fResult.Invoke();

            }
            Console.WriteLine($"****************ThreadBtn_Click End    ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
        }
        /// <summary>
        /// 如何保证不卡顿界面；让两个委托中的任务有序执行？
        /// </summary>
        /// <param name="threadStart"></param>
        /// <param name="action"></param>
        private void ThreadWithCallBack(Action threadStart, Action action)
        {
            ///在这里开启一个线程去执行这两个委托
            ThreadStart start = () =>
              {
                  threadStart.Invoke();
                  action.Invoke();
              };
            new Thread(start).Start();
        }

        private Func<T> ThreadWithReturn<T>(Func<T> func)
        {
            T t = default(T);
            ThreadStart threadStart = () =>
            {
                t = func.Invoke();
            };
            Thread thread = new Thread(threadStart);
            thread.Start();
            return new Func<T>(() =>
            {
                thread.Join();
                return t;
            });
        }

        /// <summary>
        ///  单纯的只是一个比较耗时的方法
        /// </summary>
        /// <param name="name"></param>
        private void DoCostTimeMethod(string name)
        {
            Console.WriteLine($"****************DoCostTimeMethod Start  {name}  ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            long lResult = 0;
            for (int i = 0; i < 1_000_000_000; i++)
            {
                lResult += i;
            }
            Thread.Sleep(5000);

            Console.WriteLine($"****************DoCostTimeMethod   End  {name}  ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
        }

        #endregion

        #region 线程池 ThreadPool 
        /// <summary>
        /// 线程池：ThreadPool
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ThreadPoolBtn_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"****************ThreadPoolBtn_Click Start ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            {
                //WaitCallback waitCallback = new WaitCallback(o =>
                //{
                //    this.DoCostTimeMethod(o.ToString()) ;
                //});
                //ThreadPool.QueueUserWorkItem(waitCallback,"极限。。。");//从线程池中获取线程 
            }
            {
                ManualResetEvent mre = new ManualResetEvent(false);
                ThreadPool.QueueUserWorkItem(o =>
                {
                    Console.WriteLine($"this is  {o}");
                    mre.Set();
                });//从线程池中获取线程 
                //线程等待
                mre.WaitOne();
            }

            Console.WriteLine($"****************ThreadPoolBtn_Click End   ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
        }
        #endregion

        #region 多线程Task
        /// <summary>
        /// C#中的多线程Task
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TaskBtn_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"****************TaskBtn_Click   Start     ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            { //如何启动线程呢？ 

                //{
                //    Task task = new Task(() => { Console.WriteLine($"this id thrad {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); });
                //    task.Start();
                //}
                //{
                //    TaskFactory taskfactory = new TaskFactory();
                //    taskfactory.StartNew(() =>
                //    {
                //        DoCostTimeMethod("Joker2鹏");
                //    });
                //}
            }
            { // 申请一个线程
                //ThreadPool.SetMaxThreads(8, 8);//设置最大线程数量;因为线程池里的线程是全局
                //如何去控制线程数量呢？
                //List<int> threadIds = new List<int>();
                //List<Task> tasklist = new List<Task>();
                //for (int i = 0; i < 50; i++)
                //{
                //    tasklist.Add(Task.Run(() => { //申请一个线程 
                //        threadIds.Add(Thread.CurrentThread.ManagedThreadId); 
                //        Console.WriteLine($" ThreadId：{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                //    }));
                //}
                //Task.WaitAll(tasklist.ToArray());//阻塞主线程等待所有子线程完成任务
                //int iResult = threadIds.Distinct().Count();//iResult=8  Task from ThradPool 
            }
            {
                //每一期高级班当学完内容以后我们最后会有几个项目落地；项目分为很多功能模块
                //讲课能不能多线程？
                //1.Richard 老师给讲课  高级班学习中  不能多线程
                //2.学习结束后，同学配合开发项目   可以多线程线程吗？可以多线程；、
                //分为小组；5个人
                //直接就是一个同学一个线程嘛！
                {
                    {
                        //List<Task> taskList = new List<Task>();
                        //TeachShow("核心语法特训");
                        //TeachShow("MVC框架深解析");
                        //TeachShow(".NetCore 跨平台开发");
                        //TeachShow("分布式架构的搭建");
                        //TeachShow("微服务架构的搭建");
                        //TeachShow("EF/EFCore");
                        //Console.WriteLine("高级班学习完成。。。准备去开发项目");
                        //TaskFactory taskFactory = Task.Factory;
                        ////Thread.Sleep(1000);
                        /////不会阻塞主线程
                        //taskList.Add(taskFactory.StartNew(() => CodingShow("fresh", "后台框架的搭建")));
                        //taskList.Add(taskFactory.StartNew(() => CodingShow("马启新", "微服务架构的搭建")));
                        //taskList.Add(taskFactory.StartNew(() => CodingShow("Joker2鹏", "微信接口")));
                        //taskList.Add(taskFactory.StartNew(() => CodingShow("Single", "Core环境搭建。。")));
                        //{

                        //    //// Task.WaitAll 可以等待所有子线程完成任务以后继续往后执行；但是会卡顿界面；
                        //    //Task.WaitAll(taskList.ToArray());// 主线程等待所有子线程完成任务
                        //    ////高级班的学员都不错，所有模块开发完毕以后，我们准备聚个餐 K 个歌。。。
                        //    //Console.WriteLine("所有模块开发完毕以后，我们准备聚个餐 K 个歌。。。");
                        //}
                        //{
                        //    ///回调：就在一堆任务执行完毕以后，去申请一个新的线程来执行新的动作
                        //    //taskFactory.ContinueWhenAll(taskList.ToArray(), t => Console.WriteLine($"所有模块开发完毕以后，我们准备聚个餐 K 个歌。。。"));

                        //    taskFactory.ContinueWhenAny(taskList.ToArray(), t => Console.WriteLine($"XXX同学开发完成以后，Ricahr 老师就准备环境部署。。。"));
                        //}
                    }
                    //如何控制线程数量；
                    //为什么要限制线程数量；申请线程执行任务也是需要耗费资源；
                    {
                        /////如果把线程控制在20个线程以内
                        //List<Task> taskList = new List<Task>();
                        //for (int i = 0; i < 10000; i++)
                        //{
                        //    if (taskList.Count(t => t.Status == TaskStatus.Running) >= 20)
                        //    {
                        //        Task.WaitAny(taskList.ToArray());//阻塞主线程 等待一个任务执行完成
                        //        taskList = taskList.Where(t => t.Status != TaskStatus.RanToCompletion && t.Status != TaskStatus.Canceled && t.Status != TaskStatus.Faulted).ToList();
                        //    }
                        //    taskList.Add(Task.Run(() =>
                        //   {
                        //       Thread.Sleep(1000);
                        //       Console.WriteLine("this is  Open");
                        //   }));
                        //    Console.WriteLine($"Thrad Count={taskList.Count()}");
                        //}
                    }

                    {//如果新申请的线程里面有返回值怎么获取呢？

                        Func<int> func = () =>
                        {
                            Thread.Sleep(3000);
                            return DateTime.Now.Year;
                        };
                        Task<int> task = new Task<int>(func);
                        task.Start();
                        int iResult = task.Result; //这里会阻塞页面的；
                    }
                }



            }

            Console.WriteLine($"****************TaskBtn_Click   End     ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
        }

        /// <summary>
        /// 单线程调用方法   讲课的方法
        /// </summary>
        /// <param name="lesson"></param>
        private void TeachShow(string lesson)
        {
            Console.WriteLine($"{lesson}开始讲。。。 ThreadId:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            long lResult = 0;
            //for (int i = 0; i < 1_000_000_000; i++)
            //{
            //    lResult += i;
            //}
            Console.WriteLine($"{lesson}讲完了。。。ThreadId:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
        }

        /// <summary>
        /// 模拟Coding过程
        /// </summary>
        /// <param name="name"></param>
        /// <param name="projectName"></param>
        private void CodingShow(string name, string projectName)
        {
            Console.WriteLine($"****************CodingShow Start  {name} {projectName}  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            long lResult = 0;
            for (int i = 0; i < 1_000_000_000; i++)
            {
                lResult += i;
            }
            Console.WriteLine($"****************CodingShow   End  {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
        }


        #endregion


        /// <summary>
        /// 多线程中的那些坑儿
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AsyncCorebtn_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"****************AsyncCorebtn_Click Start  ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");

            #region 多线程异常处理
            {
                //1.单线程中如何处理异常？ try-catch
                //多线程内部如果发生异常之后，就直接吞掉；
                //2.Task.WaitAll(taskList.ToArray()可以捕捉到线程内部的异常
                //List<Task> taskList = new List<Task>();
                //for (int i = 0; i < 20; i++)
                //{
                //    try
                //    {
                //        string str = $"AsyncCorebtn_Click_{i}";
                //        taskList.Add(Task.Run(() =>
                //        {
                //            Console.WriteLine($"{str} Strat {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                //            if (str.Equals("AsyncCorebtn_Click_8"))
                //            {
                //                throw new Exception("AsyncCorebtn_Click_8异常了。。。");
                //            }
                //            else if (str.Equals("AsyncCorebtn_Click_11"))
                //            {
                //                throw new Exception("AsyncCorebtn_Click_11。。。");
                //            }
                //            else if (str.Equals("AsyncCorebtn_Click_15"))
                //            {
                //                throw new Exception("AsyncCorebtn_Click_15。。。");
                //            }
                //            Console.WriteLine($"{str} end {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                //        }));

                //        Task.WaitAll(taskList.ToArray());
                //    }
                //    catch (AggregateException exa)//异常匹配是先具体  再抽象
                //    {
                //        foreach (var exception in exa.InnerExceptions)
                //        {
                //            Console.WriteLine(exception.Message);
                //        } 
                //    }
                //    catch (Exception ex)
                //    {
                //        Console.WriteLine(ex.Message);
                //    }
                //}

            }
            //再实际开发中，如果多个线程去处理业务，如果有一个线程发生异常之后，我们其实更希望让其他别的线程能够停下来！
            #endregion

            //1.定义一个中间值/不安全 
            //2.CancellationTokenSource  有一个IsCancellationRequested bool值，默认为false; 同时提供一个Cancel() 方法用来充值有一个IsCancellationRequested false--true
            //3.线程不能从外部终止，只能自己终止自己！
            //4.CancellationTokenSource+业务判断实现线程取消


            //既然需要线程取消；正常情况能不能让还没有开启的线程直接就不开启了？ 
            //
            //Thread thread = null;
            //thread.Abort();
            #region 线程取消
            {
                //CancellationTokenSource cts = new CancellationTokenSource();
                ////bool isOk = true;  
                //for (int i = 0; i < 200; i++)
                //{
                //    string str = $"AsyncCorebtn_Click_{i}";

                //    Task.Run(() =>
                //     { 
                //         Thread.Sleep(new Random().Next(10 * 5, 10 * 10));
                //         try
                //         {
                //             Console.WriteLine($"{str} start.."); 
                //             if (str.Equals("AsyncCorebtn_Click_8"))
                //             {
                //                 throw new Exception("AsyncCorebtn_Click_8异常了。。。");
                //             }
                //             if (cts.IsCancellationRequested == false)
                //             {
                //                 Console.WriteLine("AsyncCorebtn_Click_ end....");
                //             }
                //             else
                //             {
                //                 Console.WriteLine("AsyncCorebtn_Click_ 取消....");
                //             }
                //         }
                //         catch (Exception ex)
                //         {
                //             cts.Cancel();//IsCancellationRequested false--true
                //        }
                //     }, cts.Token);
                //} 
            }
            #endregion

            #region 临时变量
            { 
                ////for (int i = 0; i < 20; i++)
                ////{
                ////    Task.Run(() => Console.WriteLine($"this is {i}"));
                ////}

                ////申请线程  不阻塞，延迟执行。。。
                ////js闭包
                //for (int i = 0; i < 20; i++)
                //{
                //    int k = i;
                //    Task.Run(() => Console.WriteLine($"this is {i} _{k}"));
                //}
            }
            #endregion

            //线程安全：如果你的代码在进程中有多个线程同时运行这一段，如果每次运行的结果都跟单线程运行时的结果一致，那么就是线程安全的 
            //线程安全问题：一般都是有全局变量/共享变量/静态变量/硬盘文件/数据库的值，只要多线程都能访问和修改

            ///怎么解决呢?
            ///1.lock
            ///2.避免去操作同一个变量
            ///3.线程安全对象  
            
            //现在大家提提问；
            
            #region 线程安全&lock 
            { 
                {
                    Num = 0;
                    for (int i = 0; i < 10000; i++)
                    {
                        Num++;
                    }
                    Console.WriteLine($"Num is {Num}");
                }
                {
                    //AsyncNum 一定再0-10000之间
                    List<Task> tasks = new List<Task>();
                    AsyncNum = 0; 
                    for (int i = 0; i < 10000; i++)
                    { 
                        tasks.Add(Task.Run(() =>
                        {
                            lock (obj_Lock) //反多线程  相当于单线程了
                            {
                               AsyncNum++;
                            }
                        })); 
                    }
                    Task.WaitAll(tasks.ToArray());
                    Console.WriteLine($"AsyncNum is {AsyncNum}");
                }
                //线程死锁  明天晚上我准备给大家分享一个多线程的实战；
            }
            #endregion

            Console.WriteLine($"****************AsyncCorebtn_Click End  ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
        }


        private static readonly object obj_Lock = new object();

        private int Num = 0;
        private int AsyncNum = 0;



    }
}
